home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / ScreenSavers / BackSpaceViews / SpinView.BackModule / SpinViewPart.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  12.2 KB  |  486 lines

  1. #import    "SpinView.h"
  2.  
  3. #import    <libc.h>
  4. #import    <math.h>
  5. #import <sys/time.h>
  6. #import    <defaults/defaults.h>
  7.  
  8. #import    <dpsclient/dpsclient.h>
  9. #import    <dpsclient/wraps.h>
  10.  
  11. #import    <appkit/Slider.h>
  12.  
  13. /**********************************************************************/
  14.  
  15. #define PI                ( 3.1415926 )
  16.  
  17. #define    DEGtoRAD(deg)    ( deg * PI/180.0 )
  18.  
  19. #define    LEFT            ( 100 )
  20. #define    RIGHT            ( 101 )
  21. #define    UP                ( 102 )
  22. #define    DOWN            ( 103 )
  23.  
  24. /**********************************************************************/
  25.  
  26. //    Looks like a set of defaults to me, how 'bout you?
  27.  
  28. static    NXDefaultsVector    SpinDefaults = 
  29.     {
  30.         { "Radius", "104" },
  31.         { "TailLength", "95" },
  32.         { "XIncrement", "1.0" },
  33.         { "YIncrement", "2.5" },
  34.         { "AngleIncrement", "38" },
  35.         { "NumberOfSpinners", "4" },
  36.         { NULL },
  37.     };
  38.  
  39. /**********************************************************************/
  40.  
  41. //    userPathData holds four endpoint coordinates for each line to draw.
  42. //    The endpoint coordinates are calculated on the fly, in drawSpinners:.
  43. //    userPathOps holds a moveto/lineto pair for each line that can be drawn.
  44. //    Both of these are passed to DPSDoUserPath(), along with the other
  45. //    neccessary arguments.  See the method drawSpinners: for the call.
  46.  
  47. static    float    userPathData[ 4*MAXNUMSPINS ];
  48.  
  49. static    char    userPathOps[ ] =
  50.     {
  51.         dps_moveto, dps_lineto,
  52.         dps_moveto, dps_lineto,
  53.         dps_moveto, dps_lineto,
  54.         dps_moveto, dps_lineto,
  55.         dps_moveto, dps_lineto,
  56.         dps_moveto, dps_lineto,
  57.         dps_moveto, dps_lineto,
  58.         dps_moveto, dps_lineto,
  59.         dps_moveto, dps_lineto,
  60.         dps_moveto, dps_lineto,
  61.         dps_moveto, dps_lineto,
  62.         dps_moveto, dps_lineto,
  63.         dps_moveto, dps_lineto,
  64.         dps_moveto, dps_lineto,
  65.         dps_moveto, dps_lineto,
  66.         dps_moveto, dps_lineto,
  67.         dps_moveto, dps_lineto,
  68.         dps_moveto, dps_lineto,
  69.         dps_moveto, dps_lineto,
  70.         dps_moveto, dps_lineto
  71.     };
  72.  
  73. static    float    Sines[ 91 ];        // Table of sines.  Filled in initFrame:
  74.  
  75. static    float    SinRad[ 91 ];        // Table of (sines * current radius).
  76.                                     // Filled in setStartPoints.
  77.  
  78. static    char    tnum[ 20 ];            // Used to modify defaults, if they
  79.                                     // need to be adjusted in the database.
  80.     
  81. /**********************************************************************/
  82.  
  83. @implementation SpinView
  84.  
  85. /**********************************************************************/
  86.  
  87. - (const char *) windowTitle
  88. {
  89.     return ( const char * ) "Larrys Wild Spinners";
  90. }
  91.  
  92. /**********************************************************************/
  93.  
  94. - ( void ) getAngleInc
  95. {
  96.     //    Gets the current angle increment from the defaults database.
  97.     //    Checks to make sure the value found is within the range delimited
  98.     //    by MAXANGLEINC and MINANGLEINC.  If the value is out of range it
  99.     //    is reset to the nearest inbounds value.
  100.         
  101.     CurAngleInc = atoi( NXGetDefaultValue( OWNER, "AngleIncrement" ) );
  102.     
  103.     if( CurAngleInc > MAXANGLEINC )
  104.     {
  105.         sprintf( tnum, "%3d", MAXANGLEINC );
  106.         NXWriteDefault( OWNER, "AngleIncrement", tnum );
  107.         CurAngleInc = MAXANGLEINC;
  108.     }
  109.     else if( CurAngleInc < MINANGLEINC )
  110.     {
  111.         sprintf( tnum, "%3d", MINANGLEINC );
  112.         NXWriteDefault( OWNER, "AngleIncrement", tnum );
  113.         CurAngleInc = MINANGLEINC;
  114.     }
  115. }
  116.  
  117. /**********************************************************************/
  118.  
  119. - ( void ) getNumSpinners
  120. {
  121.     //    Gets the current number of spinners from the defaults database.
  122.     //    Checks to make sure the value found is within the range delimited
  123.     //    by MAXNUMSPINS and MINNUMSPINS.  If the value is out of range it
  124.     //    is reset to the nearest inbounds value.
  125.         
  126.     CurNumSpins = atoi( NXGetDefaultValue( OWNER, "NumberOfSpinners" ) );
  127.     
  128.     if( CurNumSpins > MAXNUMSPINS )
  129.     {
  130.         sprintf( tnum, "%2d", MAXNUMSPINS );
  131.         NXWriteDefault( OWNER, "NumberOfSpinners", tnum );
  132.         CurNumSpins = MAXNUMSPINS;
  133.     }
  134.     else if( CurNumSpins < MINNUMSPINS )
  135.     {
  136.         sprintf( tnum, "%2d", MINNUMSPINS );
  137.         NXWriteDefault( OWNER, "NumberOfSpinners", tnum );
  138.         CurNumSpins = MINNUMSPINS;
  139.     }
  140. }
  141.  
  142. /**********************************************************************/
  143.  
  144. - ( void ) getRadius
  145. {
  146.     //    Gets the current circle radius from the defaults database.
  147.     //    Checks to make sure the value found is within the range delimited
  148.     //    by MAXRADIUS and MINRADIUS.  If the value is out of range it
  149.     //    is reset to the nearest inbounds value.
  150.         
  151.     CurRadius = atoi( NXGetDefaultValue( OWNER, "Radius" ) );
  152.     
  153.     if( CurRadius > MAXRADIUS )
  154.     {
  155.         sprintf( tnum, "%3d", MAXRADIUS );
  156.         NXWriteDefault( OWNER, "Radius", tnum );
  157.         CurRadius = MAXRADIUS;
  158.     }
  159.     else if( CurRadius < MINRADIUS )
  160.     {
  161.         sprintf( tnum, "%3d", MINRADIUS );
  162.         NXWriteDefault( OWNER, "Radius", tnum );
  163.         CurRadius = MINRADIUS;
  164.     }
  165. }
  166.  
  167. /**********************************************************************/
  168.  
  169. - ( void ) getTailLength 
  170. {
  171.     //    Gets the current tail length from the defaults database.
  172.     //    Checks to make sure the value found is within the range delimited
  173.     //    by MAXTAILLEN and MINTAILLEN.  If the value is out of range it
  174.     //    is reset to the nearest inbounds value.
  175.         
  176.     CurTailLen = atoi( NXGetDefaultValue( OWNER, "TailLength" ) );
  177.     
  178.     if( CurTailLen > MAXTAILLEN )
  179.     {
  180.         sprintf( tnum, "%3d", MAXTAILLEN );
  181.         NXWriteDefault( OWNER, "TailLength", tnum );
  182.         CurTailLen = MAXTAILLEN;
  183.     }
  184.     else if( CurTailLen < MINTAILLEN )
  185.     {
  186.         sprintf( tnum, "%3d", MINTAILLEN );
  187.         NXWriteDefault( OWNER, "TailLength", tnum );
  188.         CurTailLen = MINTAILLEN;
  189.     }
  190. }
  191.  
  192. /**********************************************************************/
  193.  
  194. - ( void ) getXIncrement 
  195. {
  196.     //    Gets the current X increment from the defaults database.
  197.     //    Checks to make sure the value found is within the range delimited
  198.     //    by MAXXINC and MINXINC.  If the value is out of range it
  199.     //    is reset to the nearest inbounds value.
  200.         
  201.     CurXInc = atof( NXGetDefaultValue( OWNER, "XIncrement" ) );
  202.     
  203.     if( CurXInc > MAXXINC )
  204.     {
  205.         sprintf( tnum, "%7.3f", MAXXINC );
  206.         NXWriteDefault( OWNER, "XIncrement", tnum );
  207.         CurXInc = MAXXINC;
  208.     }
  209.     else if( CurXInc < MINXINC )
  210.     {
  211.         sprintf( tnum, "%7.3f", MINXINC );
  212.         NXWriteDefault( OWNER, "XIncrement", tnum );
  213.         CurXInc = MINXINC;
  214.     }
  215. }
  216.  
  217. /**********************************************************************/
  218.  
  219. - ( void ) getYIncrement 
  220. {
  221.     //    Gets the current Y increment from the defaults database.
  222.     //    Checks to make sure the value found is within the range delimited
  223.     //    by MAXYINC and MINYINC.  If the value is out of range it
  224.     //    is reset to the nearest inbounds value.
  225.         
  226.     CurYInc = atof( NXGetDefaultValue( OWNER, "YIncrement" ) );
  227.     
  228.     if( CurYInc > MAXYINC )
  229.     {
  230.         sprintf( tnum, "%7.3f", MAXYINC );
  231.         NXWriteDefault( OWNER, "YIncrement", tnum );
  232.         CurYInc = MAXYINC;
  233.     }
  234.     else if( CurYInc < MINYINC )
  235.     {
  236.         sprintf( tnum, "%7.3f", MINYINC );
  237.         NXWriteDefault( OWNER, "YIncrement", tnum );
  238.         CurYInc = MINYINC;
  239.     }
  240. }
  241.  
  242. /**********************************************************************/
  243.  
  244. - initFrame : ( const NXRect * ) frameRect
  245. {
  246.     //    How many miscellaneous setup things can you 
  247.     //    put into a single method, geez!
  248.     
  249.     struct    timeval        tp;
  250.     struct    timezone    tzp;
  251.     int    i;
  252.  
  253.     [ super initFrame : frameRect ];
  254.     
  255.     [ self setOpaque : YES ];
  256.     [ self setClipping : NO ];
  257.     
  258.     NXRegisterDefaults( OWNER, SpinDefaults );
  259.     
  260.     [ self getNumSpinners ];
  261.     [ self getRadius ];
  262.     [ self getAngleInc ];
  263.     [ self getTailLength ];
  264.     [ self getXIncrement ];
  265.     [ self getYIncrement ];
  266.     
  267.     PSsetlinewidth( 0.15 );
  268.  
  269.     gettimeofday( &tp, &tzp );
  270.     srandom( ( int ) tp.tv_sec + tp.tv_usec );
  271.             
  272.     for( i = 0; i < 91; ++i )
  273.         Sines[ i ] = sin( DEGtoRAD(i) );        // Fill in sines table.
  274.  
  275.     [ self resetSpinners ];
  276.     
  277.     return self;
  278. }
  279.  
  280. /**********************************************************************/
  281.  
  282. - newWindow
  283. {
  284.     [ self resetSpinners ];
  285.     
  286.     return self;
  287. }
  288.  
  289. /**********************************************************************/
  290.  
  291. - sizeTo : ( NXCoord ) width : ( NXCoord ) height
  292. {
  293.     [ super sizeTo : width : height ];
  294.     
  295.     [ self resetSpinners ];
  296.     
  297.     return self;
  298. }
  299.  
  300. /**********************************************************************/
  301.  
  302. - ( void ) setStartPoints
  303. {
  304.     int    i;
  305.     
  306.     for( i = 0; i < CurNumSpins; ++i )
  307.     {
  308.         headSpin[i].x = tailSpin[i].x = random() % ( (int) bounds.size.width);
  309.         headSpin[i].y = tailSpin[i].y = random() % ( (int) bounds.size.height);
  310.     }
  311.     
  312.     //    Reset the color cycle.
  313.     
  314.     r = 0;
  315.     g = ( 2 * PI ) / 3;
  316.     
  317.     // Everytime the current radius changes the SinRad[] table and the
  318.     // bounding box need to be updated.  These should probably be done
  319.     // in the setRadius: method, but it's so fast I don't think it matters.
  320.     
  321.     for( i = 0; i < 91; ++i )
  322.         SinRad[ i ] = CurRadius * Sines[ i ];
  323.  
  324.     // Setting the bounding box this way gives the spinners a more consistent
  325.     // feel.  When there is only one spinner displayed the speed of the spinner
  326.     // appears near the speed of having 20 spinners on the screen.  Using a
  327.     // consistent bounding box has a lot to do with it.
  328.     // The 30 is a fudge factor, I guess I should figure out the real value
  329.     // at some point.
  330.     
  331.     Bbox[ 0 ] = bounds.origin.x - ( CurRadius + 30 );
  332.     Bbox[ 1 ] = bounds.origin.y - ( CurRadius + 30 );
  333.     Bbox[ 2 ] = bounds.origin.x + bounds.size.width + ( CurRadius + 30 );
  334.     Bbox[ 3 ] = bounds.origin.y + bounds.size.height + ( CurRadius + 30 );
  335.     
  336. }
  337.  
  338. /**********************************************************************/
  339.  
  340. - ( void ) resetSpinners
  341. {
  342.     // This method realigns the head and tail spinner data structures.
  343.     // Each time a control is adjusted this method is called.  It is much
  344.     // simpler to reset everything after a control is adjusted than
  345.     // it is to figure out how to align things on the fly.
  346.     
  347.     SPINNER    *hs;
  348.     SPINNER    *ts;
  349.     int        iMod2;
  350.     int        i;
  351.     
  352.     [ self setStartPoints ];
  353.     
  354.     hs = headSpin;    // Moving pointers through the arrays is a lot faster.
  355.     ts = tailSpin;
  356.     
  357.     for( i = 0; i < CurNumSpins; ++i, ++hs, ++ts )
  358.     {
  359.         hs->a  = ts->a  = random() % 360;
  360.         hs->ix = ts->ix = CurXInc;
  361.         hs->iy = ts->iy = CurYInc;
  362.         
  363.         iMod2 = i % 2;
  364.         hs->dx = ts->dx = ( ( iMod2 ) ? RIGHT : LEFT );
  365.         hs->dy = ts->dy = ( ( iMod2 ) ? DOWN : UP );
  366.     }
  367.     
  368.     if( tailSlider )
  369.         CurTailLen = [ tailSlider intValue ];
  370. }
  371.  
  372. /**********************************************************************/
  373.     
  374. - ( void ) moveSpinner : ( SPINNER * ) spin
  375. {
  376.     spin->a = ( spin->a + CurAngleInc ) % 360;
  377.     
  378.     if( spin->x >= bounds.size.width )
  379.         spin->dx = LEFT;
  380.     else if( spin->x <= 0 )
  381.         spin->dx = RIGHT;
  382.     
  383.     spin->x += ( spin->dx == RIGHT ) ? spin->ix : -spin->ix;
  384.  
  385.     if( spin->y >= bounds.size.height )
  386.         spin->dy = DOWN;
  387.     else if( spin->y <= 0 )
  388.         spin->dy = UP;
  389.     
  390.     spin->y += ( spin->dy == UP ) ? spin->iy : -spin->iy;
  391. }
  392.  
  393. /**********************************************************************/
  394.  
  395. - ( void ) drawSpinners : ( SPINNER * ) spin
  396. {
  397.     // I know this looks a little complicated, but it really
  398.     // just builds a user path.  It fills the userPathData[] array
  399.     // with enough endpoints to draw CurNumSpins lines.  Once userPathData[]
  400.     // is filled its passed to DPSDoUserPath(), along with the count of
  401.     // points in the array.
  402.     
  403.     float    offset1;
  404.     float    offset2;
  405.     int        i, j;
  406.     
  407.     for( i = 0, j = 0; i < CurNumSpins; ++i, ++spin )
  408.     {
  409.         if( spin->a >= 270 )
  410.         {
  411.             offset1 = -SinRad[ 360 - spin->a ];
  412.             offset2 =  SinRad[ spin->a - 270 ];
  413.         }
  414.         else if( spin->a >= 180 )
  415.         {
  416.             offset1 = -SinRad[ spin->a - 180 ];
  417.             offset2 = -SinRad[ 270 - spin->a ];
  418.         }
  419.         else if( spin->a >= 90 )
  420.         {
  421.             offset1 =  SinRad[ 180 - spin->a ];
  422.             offset2 = -SinRad[ spin->a - 90 ];
  423.         }
  424.         else
  425.         {
  426.             offset1 = SinRad[ spin->a ];
  427.             offset2 = SinRad[ 90 - spin->a ];
  428.         }
  429.         
  430.         userPathData[ j++ ] = spin->x + offset2;
  431.         userPathData[ j++ ] = spin->y + offset1;
  432.         userPathData[ j++ ] = spin->x - offset2;
  433.         userPathData[ j++ ] = spin->y - offset1;
  434.     
  435.         [ self moveSpinner : spin ];
  436.     }
  437.  
  438.     DPSDoUserPath( userPathData, j, dps_float, userPathOps,
  439.                    CurNumSpins*2, Bbox, dps_ustroke );
  440. }
  441.  
  442. /**********************************************************************/
  443.  
  444. - oneStep
  445. {
  446.     if( CurTailLen-- <= 0 )
  447.     {
  448.         PSsetrgbcolor( 0.0, 0.0, 0.0 );
  449.  
  450.         [ self drawSpinners : tailSpin ];
  451.     }
  452.     
  453. //    PSsetgray( NX_WHITE );        // Used only for testing.
  454.     
  455.     r -= ( r > 2 * PI ) ? 2 * PI : -0.008;
  456.     g -= ( g > 2 * PI ) ? 2 * PI : -0.02;
  457.     b -= ( b > 2 * PI ) ? 2 * PI : -0.01;
  458.     
  459.     PSsetrgbcolor( ( cos( r ) + 1.0 ) * .5,
  460.                    ( cos( g ) + 1.0 ) * .5,
  461.                    ( cos( b ) + 1.0 ) * .5 );  
  462.                 
  463.     [ self drawSpinners : headSpin ];
  464.  
  465.     return self;
  466. }
  467.  
  468. /**********************************************************************/
  469.  
  470. - drawSelf : ( NXRect * ) rect : ( int ) count
  471. {    
  472.     if (!rect || !count)
  473.         return self;
  474.         
  475.     PSsetgray( NX_BLACK );
  476.     
  477.     NXRectFill( rect );
  478.  
  479.     return self;
  480.     
  481. }
  482.  
  483. /**********************************************************************/
  484.  
  485. @end
  486.